home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / Benchmark / Profiler.php < prev    next >
Encoding:
PHP Script  |  2006-04-07  |  13.0 KB  |  448 lines

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PEAR :: Benchmark                                                    |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 2002-2006 Matthias Englert <Matthias.Englert@gmx.de>.  |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to the New BSD license, That is bundled  |
  9. // | with this package in the file LICENSE, and is available through      |
  10. // | the world-wide-web at                                                |
  11. // | http://www.opensource.org/licenses/bsd-license.php                   |
  12. // | If you did not receive a copy of the new BSDlicense and are unable   |
  13. // | to obtain it through the world-wide-web, please send a note to       |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. //
  17. // $Id: Profiler.php,v 1.19 2006/03/01 19:26:09 anant Exp $
  18. //
  19.  
  20. require_once 'PEAR.php';
  21.  
  22. /**
  23.  * Provides timing and profiling information.
  24.  *
  25.  * Example 1: Automatic profiling start, stop, and output.
  26.  *
  27.  * <code>
  28.  * <?php
  29.  * require_once 'Benchmark/Profiler.php';
  30.  *
  31.  * $profiler = new Benchmark_Profiler(TRUE);
  32.  *
  33.  * function myFunction() {
  34.  *     global $profiler;
  35.  *     $profiler->enterSection('myFunction');
  36.  *     //do something
  37.  *     $profiler->leaveSection('myFunction');
  38.  *     return;
  39.  * }
  40.  *
  41.  * //do something
  42.  * myFunction();
  43.  * //do more
  44.  * ?>
  45.  * </code>
  46.  *
  47.  * Example 2: Manual profiling start, stop, and output.
  48.  *
  49.  * <code>
  50.  * <?php
  51.  * require_once 'Benchmark/Profiler.php';
  52.  *
  53.  * $profiler = new Benchmark_Profiler();
  54.  *
  55.  * function myFunction() {
  56.  *     global $profiler;
  57.  *     $profiler->enterSection('myFunction');
  58.  *     //do something
  59.  *     $profiler->leaveSection('myFunction');
  60.  *     return;
  61.  * }
  62.  *
  63.  * $profiler->start();
  64.  * //do something
  65.  * myFunction();
  66.  * //do more
  67.  * $profiler->stop();
  68.  * $profiler->display();
  69.  * ?>
  70.  * </code>
  71.  *
  72.  * @author    Matthias Englert <Matthias.Englert@gmx.de>
  73.  * @copyright Copyright © 2002-2005 Matthias Englert <Matthias.Englert@gmx.de>
  74.  * @license   http://www.php.net/license/3_0.txt The PHP License, Version 3.0
  75.  * @category  Benchmarking
  76.  * @package   Benchmark
  77.  * @since     1.2.0
  78.  */
  79. class Benchmark_Profiler extends PEAR {
  80.     /**
  81.      * Contains the total ex. time of each section
  82.      *
  83.      * @var    array
  84.      * @access private
  85.      */
  86.     var $_sections = array();
  87.  
  88.     /**
  89.      * Calling stack
  90.      *
  91.      * @var    array
  92.      * @access private
  93.      */
  94.     var $_stack = array();
  95.  
  96.     /**
  97.      * Notes how often a section was entered
  98.      *
  99.      * @var    array
  100.      * @access private
  101.      */
  102.     var $_numberOfCalls = array();
  103.  
  104.     /**
  105.      * Notes for each section how much time is spend in sub-sections
  106.      *
  107.      * @var    array
  108.      * @access private
  109.      */
  110.     var $_subSectionsTime = array();
  111.  
  112.     /**
  113.      * Notes for each section how often it calls which section
  114.      *
  115.      * @var    array
  116.      * @access private
  117.      */
  118.     var $_calls = array();
  119.  
  120.     /**
  121.      * Notes for each section how often it was called by which section
  122.      *
  123.      * @var    array
  124.      * @access private
  125.      */
  126.     var $_callers = array();
  127.  
  128.     /**
  129.      * Auto-starts and stops profiler
  130.      *
  131.      * @var    boolean
  132.      * @access private
  133.      */
  134.     var $_auto = FALSE;
  135.  
  136.     /**
  137.      * Max marker name length for non-html output
  138.      *
  139.      * @var    integer
  140.      * @access private
  141.      */
  142.     var $_maxStringLength = 0;
  143.  
  144.     /**
  145.      * Constructor, starts profiling recording
  146.      *
  147.      * @access public
  148.      */
  149.     function Benchmark_Profiler($auto = FALSE) {
  150.         $this->_auto = $auto;
  151.  
  152.         if ($this->_auto) {
  153.             $this->start();
  154.         }
  155.  
  156.         $this->PEAR();
  157.     }
  158.  
  159.     /**
  160.      * Destructor, stops profiling recording
  161.      *
  162.      * @access private
  163.      */
  164.     function _Benchmark_Profiler() {
  165.         if (isset($this->_auto) && $this->_auto) {
  166.             $this->stop();
  167.             $this->display();
  168.         }
  169.     }
  170.  
  171.     /**
  172.      * Returns profiling informations for a given section.
  173.      *
  174.      * @param  string $section
  175.      * @return array
  176.      * @access public
  177.      */
  178.     function getSectionInformations($section = 'Global') {
  179.         if (isset($this->_sections[$section])) {
  180.             $calls = array();
  181.  
  182.             if (isset($this->_calls[$section])) {
  183.                 $calls = $this->_calls[$section];
  184.             }
  185.  
  186.             $callers = array();
  187.  
  188.             if (isset($this->_callers[$section])) {
  189.                 $callers = $this->_callers[$section];
  190.             }
  191.  
  192.             $informations = array();
  193.  
  194.             $informations['time']       = $this->_sections[$section];
  195.             if (isset($this->_sections['Global'])) {
  196.                 $informations['percentage'] = number_format(100 * $this->_sections[$section] / $this->_sections['Global'], 2, '.', '');
  197.             } else {
  198.                 $informations['percentage'] = 'N/A';
  199.             }
  200.             $informations['calls']      = $calls;
  201.             $informations['num_calls']  = $this->_numberOfCalls[$section];
  202.             $informations['callers']    = $callers;
  203.  
  204.               if (isset($this->_subSectionsTime[$section])) {
  205.                 $informations['netto_time'] = $this->_sections[$section] - $this->_subSectionsTime[$section];
  206.               } else {
  207.                 $informations['netto_time'] = $this->_sections[$section];
  208.               }
  209.  
  210.             return $informations;
  211.         } else {
  212.             $this->raiseError("The section '$section' does not exists.\n", NULL, PEAR_ERROR_TRIGGER, E_USER_WARNING);
  213.         }
  214.     }
  215.  
  216.     /**
  217.      * Returns profiling informations for all sections.
  218.      *
  219.      * @return array
  220.      * @access public
  221.      */
  222.     function getAllSectionsInformations() {
  223.         $informations = array();
  224.  
  225.         foreach($this->_sections as $section => $time) {
  226.             $informations[$section] = $this->getSectionInformations($section);
  227.         }
  228.  
  229.         return $informations;
  230.     }
  231.  
  232.     /**
  233.      * Returns formatted profiling information.
  234.      *
  235.      * @param  string output format (auto, plain or html), default auto
  236.      * @see    display()
  237.      * @access private
  238.      */
  239.     function _getOutput($format) {
  240.         
  241.         /* Quickly find out the maximun length: Ineffecient, but will do for now! */
  242.         $informations = $this->getAllSectionsInformations();
  243.         $names = array_keys($informations);
  244.         
  245.         $maxLength = 0;
  246.         foreach ($names as $name)
  247.         {
  248.             if ($maxLength < strlen($name)) {
  249.                 $maxLength = strlen($name);
  250.             }
  251.         }
  252.         $this->_maxStringLength = $maxLength;
  253.  
  254.         if ($format == 'auto') {
  255.             if (function_exists('version_compare') &&
  256.                 version_compare(phpversion(), '4.1', 'ge')) {
  257.                 $format = isset($_SERVER['SERVER_PROTOCOL']) ? 'html' : 'plain';
  258.             } else {
  259.                 global $HTTP_SERVER_VARS;
  260.                 $format = isset($HTTP_SERVER_VARS['SERVER_PROTOCOL']) ? 'html' : 'plain';
  261.             }
  262.         }
  263.  
  264.         if ($format == 'html') {
  265.             $out = '<table style="border: 1px solid #000000; ">'."\n";
  266.             $out .=
  267.                 '<tr><td> </td><td align="center"><b>total ex. time</b></td>'.
  268.                 '<td align="center"><b>netto ex. time</b></td>'.
  269.                 '<td align="center"><b>#calls</b></td><td align="center"><b>%</b></td>'.
  270.                 '<td align="center"><b>calls</b></td><td align="center"><b>callers</b></td></tr>'.
  271.                 "\n";
  272.         } else {
  273.             $dashes = $out = str_pad("\n", ($this->_maxStringLength + 75), '-', STR_PAD_LEFT);
  274.             $out .= str_pad('Section', $this->_maxStringLength + 10);
  275.             $out .= str_pad("Total Ex Time", 22);
  276.             $out .= str_pad("Netto Ex Time", 22);
  277.             $out .= str_pad("#Calls", 10);
  278.             $out .= "Percentage\n";
  279.             $out .= $dashes;
  280.         }
  281.            
  282.         foreach($informations as $name => $values) {
  283.             $percentage = $values['percentage'];
  284.             $calls_str = "";
  285.  
  286.             foreach($values['calls'] as $key => $val) {
  287.                 if ($calls_str) {
  288.                     $calls_str .= ", ";
  289.                 }
  290.  
  291.                 $calls_str .= "$key ($val)";
  292.             }
  293.  
  294.             $callers_str = "";
  295.  
  296.             foreach($values['callers'] as $key => $val) {
  297.                 if ($callers_str) {
  298.                     $callers_str .= ", ";
  299.                     }
  300.  
  301.                 $callers_str .= "$key ($val)";
  302.             }
  303.  
  304.             if ($format == 'html') {
  305.                 $out .= "<tr><td><b>$name</b></td><td>{$values['time']}</td><td>{$values['netto_time']}</td><td>{$values['num_calls']}</td>";
  306.                 if (is_numeric($values['percentage'])) {
  307.                     $out .= "<td align=\"right\">{$values['percentage']}%</td>\n";
  308.                 } else {
  309.                     $out .= "<td align=\"right\">{$values['percentage']}</td>\n";
  310.                 }
  311.                 $out .= "<td>$calls_str</td><td>$callers_str</td></tr>";
  312.             } else {
  313.                 $out .= str_pad($name, $this->_maxStringLength + 10);
  314.                 $out .= str_pad($values['time'], 22);
  315.                 $out .= str_pad($values['netto_time'], 22);
  316.                 $out .= str_pad($values['num_calls'], 10);
  317.                 if (is_numeric($values['percentage'])) {
  318.                     $out .= str_pad($values['percentage']."%\n", 8, ' ', STR_PAD_LEFT);
  319.                 } else {
  320.                     $out .= str_pad($values['percentage']."\n", 8, ' ', STR_PAD_LEFT);
  321.                 }
  322.             }
  323.         }
  324.         
  325.         if ($format == 'html') {
  326.             return $out . '</table>';
  327.         } else {
  328.             return $out;
  329.         }
  330.     }
  331.  
  332.     /**
  333.      * Returns formatted profiling information.
  334.      *
  335.      * @param  string output format (auto, plain or html), default auto
  336.      * @access public
  337.      */
  338.     function display($format = 'auto') {
  339.         echo $this->_getOutput($format);
  340.     }
  341.  
  342.     /**
  343.      * Enters "Global" section.
  344.      *
  345.      * @see    enterSection(), stop()
  346.      * @access public
  347.      */
  348.     function start() {
  349.         $this->enterSection('Global');
  350.     }
  351.  
  352.     /**
  353.      * Leaves "Global" section.
  354.      *
  355.      * @see    leaveSection(), start()
  356.      * @access public
  357.      */
  358.     function stop() {
  359.         $this->leaveSection('Global');
  360.     }
  361.  
  362.     /**
  363.      * Enters code section.
  364.      *
  365.      * @param  string  name of the code section
  366.      * @see    start(), leaveSection()
  367.      * @access public
  368.      */
  369.     function enterSection($name) {
  370.         if (count($this->_stack)) {
  371.             if (isset($this->_callers[$name][$this->_stack[count($this->_stack) - 1]["name"]])) {
  372.                 $this->_callers[$name][$this->_stack[count($this->_stack) - 1]["name"]]++;
  373.             } else {
  374.                 $this->_callers[$name][$this->_stack[count($this->_stack) - 1]["name"]] = 1;
  375.             }
  376.  
  377.             if (isset($this->_calls[$this->_stack[count($this->_stack) - 1]["name"]][$name])) {
  378.                 $this->_calls[$this->_stack[count($this->_stack) - 1]["name"]][$name]++;
  379.             } else {
  380.                 $this->_calls[$this->_stack[count($this->_stack) - 1]["name"]][$name] = 1;
  381.             }
  382.         } else {
  383.             if ($name != 'Global') {
  384.                 $this->raiseError("tried to enter section ".$name." but profiling was not started\n", NULL, PEAR_ERROR_DIE);
  385.             }
  386.         }
  387.  
  388.         if (isset($this->_numberOfCalls[$name])) {
  389.             $this->_numberOfCalls[$name]++;
  390.         } else {
  391.             $this->_numberOfCalls[$name] = 1;
  392.         }
  393.  
  394.         array_push($this->_stack, array("name" => $name, "time" => $this->_getMicrotime()));
  395.     }
  396.  
  397.     /**
  398.      * Leaves code section.
  399.      *
  400.      * @param  string  name of the marker to be set
  401.      * @see     stop(), enterSection()
  402.      * @access public
  403.      */
  404.     function leaveSection($name) {
  405.         $microtime = $this->_getMicrotime();
  406.  
  407.         if (!count($this->_stack)) {
  408.             $this->raiseError("tried to leave section ".$name." but profiling was not started\n", NULL, PEAR_ERROR_DIE);
  409.         }
  410.  
  411.         $x = array_pop($this->_stack);
  412.  
  413.         if ($x["name"] != $name) {
  414.             $this->raiseError("reached end of section $name but expecting end of " . $x["name"]."\n", NULL, PEAR_ERROR_DIE);
  415.         }
  416.  
  417.         if (isset($this->_sections[$name])) {
  418.             $this->_sections[$name] += $microtime - $x["time"];
  419.         } else {
  420.             $this->_sections[$name] = $microtime - $x["time"];
  421.         }
  422.  
  423.         $parent = array_pop($this->_stack);
  424.  
  425.           if (isset($parent)) {
  426.             if (isset($this->_subSectionsTime[$parent['name']])) {
  427.                 $this->_subSectionsTime[$parent['name']] += $microtime - $x['time'];
  428.             } else {
  429.                 $this->_subSectionsTime[$parent['name']] = $microtime - $x['time'];
  430.             }
  431.  
  432.             array_push($this->_stack, $parent);
  433.         }
  434.     }
  435.  
  436.     /**
  437.      * Wrapper for microtime().
  438.      *
  439.      * @return float
  440.      * @access private
  441.      * @since  1.3.0
  442.      */
  443.     function _getMicrotime() {
  444.         $microtime = explode(' ', microtime());
  445.         return $microtime[1] . substr($microtime[0], 1);
  446.     }
  447. }
  448.